#include "cache.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <algorithm>

using namespace std;

/** Usage: produce help message */
void usage()
{
  fprintf(stderr, "USAGE:\n"
          "To try a set of parameters\n\n"
          "  ./cache C B S WP V < {trace_file} > {output_log_file}\n\n"
          "WP = 0(wtna) --> WTNA, WP = 1(wbwa) --> WBWA.\n\n"
          "To try all possible parameters (limit 48KB)\n\n"
          "  ./cache {trace_file}\n\n"
          "{trace_file}-PARAM.csv will contain all parameters and their statistics.\n\n");
}

/** Delay time with respect to block size */
double delayTime(int B) {
  if (B <= 3) return 13;
  else if (B == 4) return 25;
  else if (B == 5) return 50;
  else if (B == 6) return 100;
  else return 200;
}

/** Run a cache simulator with input from <fname>
 *  and report statistics to <fname>-param.csv (appending)
 */
void tryCache(Cache& c, const char* fname)
{
  std::ifstream f(fname);
  char line[100];
  char rw;
  unsigned int addr;
  while (!f.eof()) {
    rw = ' ';
    f >> rw >> std::hex >> addr;  // read r|w addr
    f.getline(line, 100);         // read \n
    if (rw != 'r' && rw != 'w') continue;
    c.access(rw, addr);
  }
  f.close();
  // calculate miss-rate, AAT, and cache size
  double missRate = (double)c.n_readmissall_ / c.n_read_;
  if (c.WP_ == WBWA)
    missRate = (double)(c.n_readmissall_+c.n_writemissall_)/ c.n_access_;
  double AAT = 2+0.2*c.S_+ missRate*delayTime(c.B_);
  int size = c.size() + c.vc_->size();

  // report statistics
  std::ofstream of((std::string(fname)+"-param.csv").c_str(), ios::app);
  of << c.C_ << "," << c.B_ << "," << c.S_<< ","  << (c.WP_==WTNA?"WTNA":"WBWA") << "," << c.V_
     << "," << AAT << "," << size << "," << missRate << "\n";
  of.close();
}

/** Try all possible parameter combinations such that cache size (including victim cache)
 *  is less than 48KB, report all statistics to <fname>-param.csv
 */
void tryAllParams(const char* fname)
{
  std::ofstream f((std::string(fname)+"-param.csv").c_str());
  f << "C,B,S,WP,V,AAT,size,miss\n";
  f.close();
  int count = 0;
  for (int C = 0; C < 16; C++) {
    for (int B = 3; B <=C && B <= 7; B++) {
      for (int S = 0; S <= C-B; S++) {
        for (int WP = 0; WP < 2; WP++) {
          for (int V = 0; V < 16-B; V++) {
            Cache c(C, B, S, WP, V, false);
            if (c.size() + c.vc_->size() > 48 * 1024 * 8) continue;
            cout << ++count << " " << C << " " << B << " " << S << " " << WP << " " << V << "\n";
            tryCache(c, fname);
          }
        }
      }
    }
  }
}

int main(int argc, char** argv)
{
  if (argc != 2 && argc != 6) {
    fprintf(stderr, "argc = %d\n", argc);
    usage();
    return 0;
  }
  if (argc == 2) {
    tryAllParams(argv[1]);
    return 0;
  }
  char line[100];
  char rw;
  unsigned int addr;
  int C, B, S, V;
  int WP;

  // process command-line parameters
  C = atoi(argv[1]);
  B = atoi(argv[2]);
  S = atoi(argv[3]);
  std::string wps(argv[4]);
  std::transform(wps.begin(), wps.end(), wps.begin(), ::tolower);
  if (wps.compare("wtna") == 0) WP = WTNA;
  else if (wps.compare("wbwa") == 0) WP = WBWA;
  else WP = atoi(argv[4]) == 0 ? WTNA : WBWA;
  V = atoi(argv[5]);
  //  Cache c("dCache", 12, 6, 2, WTNA, 4);  // go_trace.txt
  //  Cache c("dCache", 10, 5, 0, WBWA, 3);  // bzip2_trace.txt
  //  Cache c("dCache", 9, 4, 1, WTNA, 3);   // gcc_trace.txt
  //  Cache c("dCache", 14, 4, 3, WBWA, 4);    // cg_trace.txt
  Cache c(C, B, S, WP, V, true);
  while (!cin.eof()) {
    rw = ' ';
    cin >> rw >> std::hex >> addr;
    cin.getline(line, 100);
    if (rw != 'r' && rw != 'w') continue;
    //cout << rw << " 0x" << std::hex << addr << "\n";
    c.access(rw, addr);
  }
  // report statistics and cache content
  printf("\nStats:\n\n");
  printf("%d\n", c.n_access_);
  printf("%d\n", c.n_read_);
  printf("%d\n", c.n_readmiss_);
  printf("%d\n", c.n_write_);
  printf("%d\n", c.n_writemiss_);
  printf("%d\n", c.n_writemiss_+c.n_readmiss_);
  printf("%d\n", c.size());
  printf("%d\n", c.n_readmissall_);
  printf("%d\n", c.n_writemissall_);
  printf("%d\n", c.n_writeback_);
  printf("%d\n", c.n_mem_);
  double missRate = (double)c.n_readmissall_ / c.n_read_;
  if (c.WP_ == WBWA)
    missRate = (double)(c.n_readmissall_+c.n_writemissall_)/ c.n_access_;
  printf("%8.8f\n", missRate);
  printf("%8.8f\n", 2+0.2*c.S_+ missRate*delayTime(c.B_));
  printf("%d\n\n", c.vc_->size());
  c.print_content("dCache");
  return 0;
}
